package com.lagou.edu.factory;

import com.google.common.base.CaseFormat;
import com.google.common.base.Strings;
import com.lagou.edu.annotation.Autowired;
import com.lagou.edu.annotation.Service;
import com.lagou.edu.annotation.Transactional;
import com.lagou.edu.pojo.BeanWrapper;
import com.lagou.edu.utils.ClassUtils;

import java.beans.IntrospectionException;
import java.beans.Introspector;
import java.beans.PropertyDescriptor;
import java.io.IOException;
import java.io.InputStream;
import java.lang.reflect.Field;
import java.lang.reflect.InvocationTargetException;
import java.lang.reflect.Method;
import java.util.*;

/**
 * @author 应癫
 * <p>
 * 工厂类，生产对象（使用反射技术）
 */
public class BeanFactory {

    /**
     * 任务一：读取解析xml，通过反射技术实例化对象并且存储待用（map集合）
     * 任务二：对外提供获取实例对象的接口（根据id获取）
     */

    private static Map<Class, BeanWrapper> class2beanWrapper = new HashMap<>();  // 存储对象

    private static Map<String, Object> beanMap = new HashMap<>();

    static {
        InputStream resourceAsStream = BeanFactory.class.getClassLoader().getResourceAsStream("application.properties");

        Properties properties = new Properties();
        try {
            properties.load(resourceAsStream);
            String packageComponentScan = properties.getProperty("package.component.scan");
            List<Class<?>> classes = ClassUtils.getClasses(packageComponentScan);

            //步骤1 ： 实例化对象和代理类
            classes.forEach(aClass -> {
                instantiateBean(aClass);
            });

            //步骤2： 设置属性
            class2beanWrapper.forEach((key, beanWrapper) -> {
                try {
                    populateBean(key);
                } catch (IntrospectionException e) {
                    e.printStackTrace();
                }
            });

            //步骤3：生成最后的map
            class2beanWrapper.forEach((key, beanWrapper) -> {
                createBeanMap(key, beanWrapper);
            });

        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    private static void createBeanMap(Class aClass, BeanWrapper beanWrapper) {

        Service serviceAnnotation = (Service) aClass.getAnnotation(Service.class);

        if (serviceAnnotation != null && !Strings.isNullOrEmpty(serviceAnnotation.value())){
            beanMap.put(upperCamel2lowerCamel(serviceAnnotation.value()), beanWrapper.getProxyInstance());
        }else {
            beanMap.put(upperCamel2lowerCamel(aClass.getSimpleName()), beanWrapper.getProxyInstance());
        }

    }


    /**
     * 填充属性
     *
     * @param aClass
     */
    private static void populateBean(Class<?> aClass) throws IntrospectionException {

        BeanWrapper beanWrapper = class2beanWrapper.get(aClass);
        Class<?> realClass = beanWrapper.getInstance().getClass();
        PropertyDescriptor[] propertyDescriptors = Introspector.getBeanInfo(realClass).getPropertyDescriptors();

        if (beanWrapper.isHavePopulateBean()){
            return;
        }

        Arrays.asList(propertyDescriptors).forEach(propertyDescriptor -> {

            Field field = null;
            try {
                field = realClass.getDeclaredField(propertyDescriptor.getName());
            } catch (NoSuchFieldException e) {
//                e.printStackTrace();
            }
            if (field == null) {
                return;
            }

            Autowired annotation = field.getAnnotation(Autowired.class);
            if (annotation == null) {
                return;
            }

            Method writeMethod = propertyDescriptor.getWriteMethod();
            try {
                BeanWrapper fieldBeanWrapper = class2beanWrapper.get(field.getGenericType());
                writeMethod.invoke(beanWrapper.getInstance(), fieldBeanWrapper.getProxyInstance());
            } catch (IllegalAccessException e) {
                e.printStackTrace();
            } catch (InvocationTargetException e) {
                e.printStackTrace();
            }


        });

        beanWrapper.updateHavePopulateBean();
    }

    /**
     * 实例化bean
     *
     * @param aClass
     */
    private static <T> T instantiateBean(Class<T> aClass) {
        try {
            //根据注解判断类是否需要实例化
            Service annotation = aClass.getAnnotation(Service.class);
            if (annotation == null) {
                return null;
            }

            //已经初始化的，不做初始化
            if (class2beanWrapper.get(aClass) != null){
                return (T)class2beanWrapper.get(aClass).getInstance();
            }

            T instance = aClass.newInstance();

            if (aClass.equals(ProxyFactory.class)){
                class2beanWrapper.put(aClass, new BeanWrapper(instance, instance, aClass));
                return instance;
            }

            Transactional annotation1 = aClass.getAnnotation(Transactional.class);
            BeanWrapper beanWrapper ;
            if (annotation1 == null ){
                beanWrapper = new BeanWrapper(instance, instance, aClass);
            }else {
                //把对象和代理对象都放进
                ProxyFactory proxyFactory = instantiateBean(ProxyFactory.class);
                beanWrapper = new BeanWrapper(instance, proxyFactory.getJdkProxy(instance), aClass);
            }
            class2beanWrapper.put(aClass, beanWrapper);

            Class<?>[] interfaces = aClass.getInterfaces();
            for (Class<?> anInterface : interfaces) {
                class2beanWrapper.put(anInterface, beanWrapper);
            }

            return instance;

        } catch (InstantiationException e) {
            e.printStackTrace();
        } catch (IllegalAccessException e) {
            e.printStackTrace();
        }
        return null;
    }

    private static String upperCamel2lowerCamel(String str) {
        return CaseFormat.UPPER_CAMEL.to(CaseFormat.LOWER_CAMEL, str);
    }


    // 任务二：对外提供获取实例对象的接口（根据id获取）
    public static Object getBean(String id) {
        return beanMap.get(id);
    }

}
